#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
阿里云盘文件操作模块
提供文件的基础操作功能
"""

import os
import json
import time
import hashlib
import logging
import requests
from typing import Dict, List, Optional, Any, Union, BinaryIO


class FileOperations:
    """阿里云盘文件操作类"""
    
    def __init__(self, client):
        """初始化文件操作
        
        Args:
            client: 阿里云盘客户端实例
        """
        self.client = client
        self.logger = client.logger
        self.session = client.session
        
        # 阿里云盘API端点
        self.api_base = "https://api.aliyundrive.com"
    
    def get_files(self, folder_id: str = "root", page: int = 1, 
                 page_size: int = 100) -> Optional[List[Dict[str, Any]]]:
        """获取文件列表
        
        Args:
            folder_id: 文件夹ID，"root"表示根目录
            page: 页码
            page_size: 每页数量
        
        Returns:
            List[Dict[str, Any]]: 文件列表
        """
        if not self.client.check_login_status():
            return None
        
        url = f"{self.api_base}/adrive/v3/file/list"
        headers = {
            'Authorization': f'Bearer {self.client.access_token}',
            'Content-Type': 'application/json'
        }
        data = {
            'drive_id': self.client.drive_id,
            'parent_file_id': folder_id,
            'limit': page_size,
            'marker': '',  # 阿里云盘使用marker进行分页
            'order_by': 'updated_at',
            'order_direction': 'DESC'
        }
        
        try:
            response = self.session.post(url, headers=headers, json=data)
            if response.status_code == 200:
                result = response.json()
                files = []
                for item in result.get('items', []):
                    files.append({
                        'id': item.get('file_id'),
                        'name': item.get('name'),
                        'size': item.get('size', 0),
                        'type': item.get('type'),  # 'file' or 'folder'
                        'md5': item.get('content_hash'),
                        'create_time': item.get('created_at'),
                        'modify_time': item.get('updated_at'),
                        'parent_id': item.get('parent_file_id'),
                        'download_url': item.get('download_url'),
                        'thumbnail': item.get('thumbnail')
                    })
                return files
            else:
                self.logger.error(f"获取文件列表失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"获取文件列表请求失败: {e}")
        
        return None
    
    def get_file_info(self, file_id: str) -> Optional[Dict[str, Any]]:
        """获取文件信息
        
        Args:
            file_id: 文件ID
        
        Returns:
            Dict[str, Any]: 文件信息
        """
        if not self.client.check_login_status():
            return None
        
        url = f"{self.api_base}/v2/file/get"
        headers = {
            'Authorization': f'Bearer {self.client.access_token}',
            'Content-Type': 'application/json'
        }
        data = {
            'drive_id': self.client.drive_id,
            'file_id': file_id
        }
        
        try:
            response = self.session.post(url, headers=headers, json=data)
            if response.status_code == 200:
                result = response.json()
                return {
                    'id': result.get('file_id'),
                    'name': result.get('name'),
                    'size': result.get('size', 0),
                    'type': result.get('type'),
                    'md5': result.get('content_hash'),
                    'create_time': result.get('created_at'),
                    'modify_time': result.get('updated_at'),
                    'parent_id': result.get('parent_file_id'),
                    'download_url': result.get('download_url'),
                    'thumbnail': result.get('thumbnail')
                }
            else:
                self.logger.error(f"获取文件信息失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"获取文件信息请求失败: {e}")
        
        return None
    
    def get_download_link(self, file_id: str) -> Optional[str]:
        """获取文件下载链接
        
        Args:
            file_id: 文件ID
        
        Returns:
            str: 下载链接
        """
        if not self.client.check_login_status():
            return None
        
        url = f"{self.api_base}/v2/file/get_download_url"
        headers = {
            'Authorization': f'Bearer {self.client.access_token}',
            'Content-Type': 'application/json'
        }
        data = {
            'drive_id': self.client.drive_id,
            'file_id': file_id
        }
        
        try:
            response = self.session.post(url, headers=headers, json=data)
            if response.status_code == 200:
                result = response.json()
                return result.get('url')
            else:
                self.logger.error(f"获取下载链接失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"获取下载链接请求失败: {e}")
        
        return None
    
    def download_file(self, file_id: str, save_path: str = None) -> Union[bool, bytes, None]:
        """下载文件
        
        Args:
            file_id: 文件ID
            save_path: 保存路径，None表示返回文件内容
        
        Returns:
            Union[bool, bytes, None]: 保存到文件返回bool，返回内容返回bytes，失败返回None
        """
        download_link = self.get_download_link(file_id)
        if not download_link:
            return None
        
        try:
            response = self.session.get(download_link, stream=True)
            if response.status_code == 200:
                if save_path:
                    # 保存到文件
                    os.makedirs(os.path.dirname(save_path), exist_ok=True)
                    with open(save_path, 'wb') as f:
                        for chunk in response.iter_content(chunk_size=8192):
                            f.write(chunk)
                    self.logger.info(f"文件下载成功: {save_path}")
                    return True
                else:
                    # 返回文件内容
                    return response.content
            else:
                self.logger.error(f"下载文件失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"下载文件请求失败: {e}")
        
        return None
    
    def create_folder(self, name: str, parent_id: str = "root") -> Optional[Dict[str, Any]]:
        """创建文件夹
        
        Args:
            name: 文件夹名称
            parent_id: 父文件夹ID
        
        Returns:
            Dict[str, Any]: 创建成功返回文件夹信息，失败返回None
        """
        if not self.client.check_login_status():
            return None
        
        url = f"{self.api_base}/adrive/v2/file/createWithFolders"
        headers = {
            'Authorization': f'Bearer {self.client.access_token}',
            'Content-Type': 'application/json'
        }
        data = {
            'drive_id': self.client.drive_id,
            'parent_file_id': parent_id,
            'name': name,
            'type': 'folder',
            'check_name_mode': 'refuse'  # 拒绝重名
        }
        
        try:
            response = self.session.post(url, headers=headers, json=data)
            if response.status_code == 201:
                result = response.json()
                self.logger.info(f"文件夹创建成功: {name}")
                return {
                    'id': result.get('file_id'),
                    'name': result.get('file_name'),
                    'type': 'folder',
                    'create_time': result.get('created_at'),
                    'parent_id': result.get('parent_file_id')
                }
            else:
                self.logger.error(f"创建文件夹失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"创建文件夹请求失败: {e}")
        
        return None
    
    def delete_file_or_folder(self, file_id: str) -> bool:
        """删除文件或文件夹
        
        Args:
            file_id: 文件或文件夹ID
        
        Returns:
            bool: 删除是否成功
        """
        if not self.client.check_login_status():
            return False
        
        # 先移动到回收站
        url = f"{self.api_base}/v2/recyclebin/trash"
        headers = {
            'Authorization': f'Bearer {self.client.access_token}',
            'Content-Type': 'application/json'
        }
        data = {
            'drive_id': self.client.drive_id,
            'file_id': file_id
        }
        
        try:
            response = self.session.post(url, headers=headers, json=data)
            if response.status_code == 200:
                self.logger.info(f"删除成功: {file_id}")
                return True
            else:
                self.logger.error(f"删除失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"删除请求失败: {e}")
        
        return False
    
    def rename_file_or_folder(self, file_id: str, new_name: str) -> bool:
        """重命名文件或文件夹
        
        Args:
            file_id: 文件或文件夹ID
            new_name: 新名称
        
        Returns:
            bool: 重命名是否成功
        """
        if not self.client.check_login_status():
            return False
        
        url = f"{self.api_base}/v3/file/update"
        headers = {
            'Authorization': f'Bearer {self.client.access_token}',
            'Content-Type': 'application/json'
        }
        data = {
            'drive_id': self.client.drive_id,
            'file_id': file_id,
            'name': new_name,
            'check_name_mode': 'refuse'
        }
        
        try:
            response = self.session.post(url, headers=headers, json=data)
            if response.status_code == 200:
                self.logger.info(f"重命名成功: {file_id} -> {new_name}")
                return True
            else:
                self.logger.error(f"重命名失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"重命名请求失败: {e}")
        
        return False
    
    def move_file(self, file_id: str, target_folder_id: str) -> bool:
        """移动文件
        
        Args:
            file_id: 文件ID
            target_folder_id: 目标文件夹ID
        
        Returns:
            bool: 移动是否成功
        """
        if not self.client.check_login_status():
            return False
        
        url = f"{self.api_base}/v3/file/move"
        headers = {
            'Authorization': f'Bearer {self.client.access_token}',
            'Content-Type': 'application/json'
        }
        data = {
            'drive_id': self.client.drive_id,
            'file_id': file_id,
            'to_parent_file_id': target_folder_id,
            'check_name_mode': 'refuse'
        }
        
        try:
            response = self.session.post(url, headers=headers, json=data)
            if response.status_code == 200:
                self.logger.info(f"文件移动成功: {file_id} -> {target_folder_id}")
                return True
            else:
                self.logger.error(f"移动文件失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"移动文件请求失败: {e}")
        
        return False
    
    def copy_file(self, file_id: str, target_folder_id: str, new_name: str = None) -> Optional[Dict[str, Any]]:
        """复制文件
        
        Args:
            file_id: 文件ID
            target_folder_id: 目标文件夹ID
            new_name: 新文件名，None表示保持原名
        
        Returns:
            Dict[str, Any]: 复制成功返回新文件信息，失败返回None
        """
        if not self.client.check_login_status():
            return None
        
        url = f"{self.api_base}/v3/file/copy"
        headers = {
            'Authorization': f'Bearer {self.client.access_token}',
            'Content-Type': 'application/json'
        }
        data = {
            'drive_id': self.client.drive_id,
            'file_id': file_id,
            'to_parent_file_id': target_folder_id,
            'auto_rename': True
        }
        
        if new_name:
            data['new_name'] = new_name
        
        try:
            response = self.session.post(url, headers=headers, json=data)
            if response.status_code == 200:
                result = response.json()
                self.logger.info(f"文件复制成功: {file_id}")
                return {
                    'id': result.get('file_id'),
                    'name': result.get('file_name'),
                    'type': 'file',
                    'parent_id': target_folder_id
                }
            else:
                self.logger.error(f"复制文件失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"复制文件请求失败: {e}")
        
        return None
    
    def calculate_md5(self, file_data: Union[str, bytes, BinaryIO]) -> str:
        """计算文件MD5值
        
        Args:
            file_data: 文件数据（文件路径、字节数据或文件对象）
        
        Returns:
            str: MD5值
        """
        md5_hash = hashlib.md5()
        
        if isinstance(file_data, str):
            # 文件路径
            with open(file_data, 'rb') as f:
                for chunk in iter(lambda: f.read(8192), b""):
                    md5_hash.update(chunk)
        elif isinstance(file_data, bytes):
            # 字节数据
            md5_hash.update(file_data)
        else:
            # 文件对象
            for chunk in iter(lambda: file_data.read(8192), b""):
                md5_hash.update(chunk)
        
        return md5_hash.hexdigest()
    
    def calculate_sha1(self, file_data: Union[str, bytes, BinaryIO]) -> str:
        """计算文件SHA1值（阿里云盘使用SHA1作为content_hash）
        
        Args:
            file_data: 文件数据（文件路径、字节数据或文件对象）
        
        Returns:
            str: SHA1值
        """
        sha1_hash = hashlib.sha1()
        
        if isinstance(file_data, str):
            # 文件路径
            with open(file_data, 'rb') as f:
                for chunk in iter(lambda: f.read(8192), b""):
                    sha1_hash.update(chunk)
        elif isinstance(file_data, bytes):
            # 字节数据
            sha1_hash.update(file_data)
        else:
            # 文件对象
            for chunk in iter(lambda: file_data.read(8192), b""):
                sha1_hash.update(chunk)
        
        return sha1_hash.hexdigest().upper()  # 阿里云盘要求大写